import re
import subprocess
import tkinter as tk
from tkinter import filedialog, messagebox
import os

def extract_addresses(log_content):
    """
    从日志中提取所有地址（MEPC、RA 和其他寄存器）。
    """
    address_pattern = r"(MEPC|RA|SP|GP|TP|T0|T1|T2|S0/FP|S1|A0|A1|A2|A3|A4|A5|A6|A7|S2|S3|S4|S5|S6|S7|S8|S9|S10|S11|T3|T4|T5|T6|MSTATUS|MTVEC|MCAUSE|MTVAL|MHARTID)\s+:\s+(0x[0-9a-fA-F]+)"
    matches = re.findall(address_pattern, log_content)
    addresses = [match[1] for match in matches]
    return addresses

def decode_addresses(elf_path, toolchain_path, addresses):
    """
    使用 addr2line 批量解码地址。
    """
    decoded_results = {}
    for address in addresses:
        try:
            result = subprocess.run(
                [toolchain_path, "-e", elf_path, address],
                stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
            )
            if result.returncode == 0:
                decoded_results[address] = result.stdout.strip()
            else:
                decoded_results[address] = f"解码错误: {result.stderr.strip()}"
        except Exception as e:
            decoded_results[address] = f"运行 addr2line 失败: {e}"
    return decoded_results

def browse_elf_file():
    """
    打开文件对话框，选择 firmware.elf 文件。
    """
    elf_path = filedialog.askopenfilename(
        title="选择 firmware.elf 文件",
        filetypes=[("ELF 文件", "*.elf"), ("所有文件", "*.*")]
    )
    elf_path_entry.delete(0, tk.END)
    elf_path_entry.insert(0, elf_path)

def browse_toolchain_file():
    """
    打开文件对话框，选择工具链路径。
    """
    toolchain_path = filedialog.askopenfilename(
        title="选择工具链路径 (riscv32-esp-elf-addr2line)",
        filetypes=[("可执行文件", "*.exe"), ("所有文件", "*.*")]
    )
    toolchain_path_entry.delete(0, tk.END)
    toolchain_path_entry.insert(0, toolchain_path)

def decode_log():
    """
    解码异常日志并显示结果。
    """
    log_content = log_text.get("1.0", tk.END).strip()
    elf_path = elf_path_entry.get().strip()
    toolchain_path = toolchain_path_entry.get().strip()

    if not log_content:
        messagebox.showerror("错误", "请粘贴异常日志。")
        return
    if not elf_path:
        messagebox.showerror("错误", "请选择 firmware.elf 文件。")
        return
    if not toolchain_path:
        messagebox.showerror("错误", "请输入工具链路径。")
        return

    addresses = extract_addresses(log_content)
    if not addresses:
        messagebox.showerror("错误", "日志中未找到有效地址。")
        return

    decoded_results = decode_addresses(elf_path, toolchain_path, addresses)
    result_text.delete("1.0", tk.END)
    for address, result in decoded_results.items():
        result_text.insert(tk.END, f"{address} -> {result}\n")

# 创建主窗口
root = tk.Tk()
root.title("ESP32-C3 异常解码工具")

# 设置窗口缩放时组件动态调整
root.grid_rowconfigure(1, weight=1)  # 日志输入框所在行
root.grid_rowconfigure(6, weight=1)  # 解码结果框所在行
root.grid_columnconfigure(1, weight=1)  # 输入框和输出框所在列

# 创建 UI 组件
tk.Label(root, text="粘贴异常日志:").grid(row=0, column=0, sticky="w", padx=10, pady=5)
log_text = tk.Text(root)
log_text.grid(row=1, column=0, columnspan=3, padx=10, pady=5, sticky="nsew")

tk.Label(root, text="firmware.elf 路径:").grid(row=2, column=0, sticky="w", padx=10, pady=5)
elf_path_entry = tk.Entry(root)
elf_path_entry.grid(row=2, column=1, padx=10, pady=5, sticky="ew")
tk.Button(root, text="浏览", command=browse_elf_file).grid(row=2, column=2, padx=10, pady=5)

tk.Label(root, text="工具链路径 (riscv32-esp-elf-addr2line):").grid(row=3, column=0, sticky="w", padx=10, pady=5)
toolchain_path_entry = tk.Entry(root)
toolchain_path_entry.grid(row=3, column=1, padx=10, pady=5, sticky="ew")
tk.Button(root, text="浏览", command=browse_toolchain_file).grid(row=3, column=2, padx=10, pady=5)

tk.Button(root, text="解码日志", command=decode_log).grid(row=4, column=0, columnspan=3, pady=10)

tk.Label(root, text="解码结果:").grid(row=5, column=0, sticky="w", padx=10, pady=5)
result_text = tk.Text(root)
result_text.grid(row=6, column=0, columnspan=3, padx=10, pady=5, sticky="nsew")

# 运行主循环
root.mainloop()